local tablet = AceLibrary("Tablet-2.0")
local dewdrop = AceLibrary("Dewdrop-2.0")
local L = AceLibrary("AceLocale-2.2"):new("TopScoreFu")
local _G = _G

-- Let's make a few functions local.
local string_format = string.format
local string_find = string.find
local string_sub = string.sub
local ipairs = ipairs
local pairs = pairs
local tonumber = tonumber
local bit_band = bit.band
local COMBATLOG_FILTER_ME = COMBATLOG_FILTER_ME
local COMBATLOG_OBJECT_TARGET = COMBATLOG_OBJECT_TARGET
local COMBATLOG_OBJECT_FOCUS = COMBATLOG_OBJECT_FOCUS
local COMBATLOG_FILTER_PVP = bit.bor(COMBATLOG_OBJECT_CONTROL_PLAYER, COMBATLOG_OBJECT_TYPE_PLAYER)
local COMBATLOG_FILTER_NPC = bit.bor(COMBATLOG_OBJECT_CONTROL_NPC, COMBATLOG_OBJECT_TYPE_NPC)

local spelldata = {
	["attack"] = GetSpellInfo(6603), -- Attack
	substitutions = {
		[GetSpellInfo(20185)] = GetSpellInfo(20165), -- 20185 Judgement of Light, 20165 Seal of Light
		[GetSpellInfo(20187)] = GetSpellInfo(21084), -- 20187 Judgement of Righteousness, 21084 Seal of Righteousness
		[GetSpellInfo(20186)] = GetSpellInfo(21066), -- 20186 Judgement of Wisdom, 20166 Seal of Wisdom
		[GetSpellInfo(20184)] = GetSpellInfo(20164), -- 20184 Judgement of Justice, 20164 Seal of Justice
		[GetSpellInfo(20425)] = GetSpellInfo(20375), -- 20425 Judgement of Command, 20375 Seal of Command
		[GetSpellInfo(21183)] = GetSpellInfo(21082), -- 21183 Judgement of the Crusader, 21082 Seal of the Crusader
		[GetSpellInfo(31804)] = GetSpellInfo(31801), -- 31804 Judgement of Vengeance, 31801 Seal of Vengeance
		[GetSpellInfo(25583)] = GetSpellInfo(8232), -- 25583 Windfury Attack, 8232 Windfury Weapon
		[GetSpellInfo(8034)] = GetSpellInfo(8033), -- 8034 Frostband Attack, 8033 Frostbrand Weapon
		[GetSpellInfo(29469)] = GetSpellInfo(8024), -- 29469 Flametongue Attack, 8024 Flametongue Weapon
		[GetSpellInfo(5857)] = GetSpellInfo(1949), -- 5857 Hellfire Effect, 1949 Hellfire
		[GetSpellInfo(9007)] = GetSpellInfo(9005), -- 9007 Pounce Bleed, 9005 Pounce
	}
}


-- A cache of the spells a player has, created at login
-- Recreated if the player learns a new spell
local spellcache

-- Vulnerable mobs table
local vulnerableMobs = {
	---------------------[[
	-- Pre-TBC Instances --
	---------------------]]
	-- Ruins of Ahn'Qiraj (AQ20)
	[15339] = true, -- Ossirian the Unscarred
	-- Blackwing Lair
	[12461] = true, -- Death Talon Overseer
	[12460] = true, -- Death Talon Wyrmguard
	[13020] = true, -- Vaelastrasz the Corrupt
	[14020] = true, -- Chromaggus
	-- Naxxramas
	[16803] = true, -- Deathknight Understudy
	[15928] = true, -- Thaddius
	---------------------[[
	--     Outlands      --
	---------------------]]
	[20132] = true, -- Netherstorm: Socrethar
	[21838] = true, -- Terokkar Forest - Skettis: Terokk
	[22992] = true, -- Terokkar Forest - Quest - The Hawk's Essence: Guardian of the Hawk
	[23228] = true, -- Ogri'la - Shartuul Event: Eye of Shartuul
	[23275] = true, -- Ogri'la - Shartuul Event: Dreadmaw
	[23230] = true, -- Ogri'la - Shartuul Event: Shartuul
	---------------------[[
	--   TBC Instances   --
	---------------------]]
	-- Karazhan
	[15688] = true, -- Terestian Illhoof
	[15691] = true, -- The Curator
	[15689] = true, -- Netherspite
	-- Magtheridon's Lair
	[17257] = true, -- Magtheridon
	-- Tempest Keep: The Eye
	[19622] = true, -- Kael'thas Sunstrider
	[20062] = true, -- Grand Astromancer Capernian
	[20060] = true, -- Lord Sanguinar
	[20063] = true, -- Master Engineer Telonicus
	[20064] = true, -- Thaladred the Darkener
	[21269] = true, -- Devastation
	[21270] = true, -- Cosmic Infuser
	[21271] = true, -- Infinity Blades
	[21274] = true, -- Staff of Disintegration
	[21272] = true, -- Warp Slicer
	[21268] = true, -- Netherstrand Longbow
	[21273] = true, -- Phaseshift Bulwark
	[21362] = true, -- Phoenix
	[21364] = true, -- Phoenix Egg
	-- Black Temple
	[22957] = true, -- Priestess of Dementia
	[22841] = true, -- Shade of Akama
	[22948] = true, -- Gurtogg Bloodboil
	[23419] = true, -- Essence of Desire
	[23418] = true, -- Essence of Suffering
	[23420] = true, -- Essence of Anger
	[22917] = true, -- Illidan Stormrage
	-- Sunwell Plateau
	[24850] = true, -- Kalecgos
	[24892] = true, -- Sathrovarr the Corruptor
	-- Magisters' Terrace
	[24745] = true, -- Pure Energy
	[24744] = true, -- Vexallus
}
-- Some bosses/mobs are only vulnerable in Heroic Mode
local heroicVulnerableMobs = {
	-- The Mechanar
	[19219] = true, -- Mechano-Lord Capacitus
}

TopScoreFu = AceLibrary("AceAddon-2.0"):new("FuBarPlugin-2.0", "AceDB-2.0", "AceEvent-2.0", "AceConsole-2.0", "Sink-1.0")
TopScoreFu.hasIcon = true
TopScoreFu.canHideText = true
TopScoreFu.clickableTooltip = true
TopScoreFu:RegisterDB("TopScoreFuDB", "TopScoreFuPCDB")
TopScoreFu:RegisterDefaults('profile', {
	sink10OutputSink = "None",
	splash = true,
	noise = true,
	screenshot = false,
	includeHeals = true,
	includeDamage = true,
	ignoreVulnerable = true,
	onlyPvP = false,
	showTrivial = false,
	posX = 0,
	posY = 0,
	filters = {}
})
TopScoreFu:RegisterDefaults('char', {
	hits = {}
})
TopScoreFu:RegisterChatCommand(L["COMMANDS"], {
	desc = L["DESCRIPTION"],
	type = "group",
	args = {},
})

function TopScoreFu:SetPosition(text)
	local _,_,x,y = string_find(text, "(%-?%d+),? (%-?%d+)")
	x, y = tonumber(x), tonumber(y)
	if x == nil or y == nil then
		self:Print(L["TEXT_SET_POSITION_ERROR"])
	else
		self.db.profile.posX = x
		self.db.profile.posY = y
		TopScoreFuSplash:ClearAllPoints()
		TopScoreFuSplash:SetPoint("CENTER", UIParent, "CENTER", self.db.profile.posX, self.db.profile.posY)
		self:Print(L["PATTERN_SET_POSITION"], x, y)
	end
end

function TopScoreFu:ResetScores()
	self.db.char.hits = {}
	self:Update()
end

function TopScoreFu:ToggleShowingSplash()
	self.db.profile.splash = not self.db.profile.splash
	return self.db.profile.splash
end

function TopScoreFu:ToggleShowingTrivial()
	self.db.profile.showTrivial = not self.db.profile.showTrivial
	self:Update()
	return self.db.profile.showTrivial
end

function TopScoreFu:TogglePlayingNoise()
	self.db.profile.noise = not self.db.profile.noise
	return self.db.profile.noise
end

function TopScoreFu:ToggleTakingScreenshots(loud)
	self.db.profile.screenshot = not self.db.profile.screenshot
	return self.db.profile.screenshot
end

function TopScoreFu:ToggleIncludingHeals()
	self.db.profile.includeHeals = not self.db.profile.includeHeals
	self:Update()
	return self.db.profile.includeHeals
end

function TopScoreFu:ToggleIncludingDamage()
	self.db.profile.includeDamage = not self.db.profile.includeDamage
	self:Update()
	return self.db.profile.includeDamage
end

function TopScoreFu:ToggleOnlyPvP()
	self.db.profile.onlyPvP = not self.db.profile.onlyPvP
	self:Update()
	return self.db.profile.onlyPvP
end

function TopScoreFu:IsFiltering(spell)
	return self.db.profile.filters[spell]
end

function TopScoreFu:ToggleFiltering(spell)
	self.db.profile.filters[spell] = not self.db.profile.filters[spell]
	self:Update()
	return self.db.profile.filters[spell]
end

function TopScoreFu:IsIgnoringVulnerable()
	return self.db.profile.ignoreVulnerable
end

function TopScoreFu:ToggleIgnoreVulnerable()
	self.db.profile.ignoreVulnerable = not self.db.profile.ignoreVulnerable
	self:Update()
	return self.db.profile.ignoreVulnerable
end

function TopScoreFu:Purge(spell)
	self.db.profile.filters[spell] = nil
	self.db.char.hits[spell] = nil
	if not next(self.db.char.hits) then
		dewdrop:Close(2)
		dewdrop:Refresh(1)
	end
	self:Update()
end

function TopScoreFu:OnInitialize()
	local frame = CreateFrame("MessageFrame", "TopScoreFuSplash", UIParent)
	frame:SetWidth(GetScreenWidth())
	frame:SetHeight(100)
	frame:SetPoint("CENTER", UIParent, "CENTER")
	frame:SetFontObject(NumberFontNormalHuge)
	frame:SetFrameStrata("HIGH")
end

function TopScoreFu:OnEnable()
	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	
	self:CreateSpellCache()
	-- This event updates the spell cache when we learn a new ability
	self:RegisterEvent("LEARNED_SPELL_IN_TAB", "CreateSpellCache")

	TopScoreFuSplash:ClearAllPoints()
	TopScoreFuSplash:SetPoint("CENTER", UIParent, "CENTER", self.db.profile.posX, self.db.profile.posY)
end

-- Small function to get an NPCID from its GUID
local function getNPCID(guid)
	return tonumber(string_sub(guid, 6, 12), 16)
end

function TopScoreFu:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	if bit_band(srcFlags, COMBATLOG_FILTER_ME) == COMBATLOG_FILTER_ME then
		local spellId, spellName, spellSchool, amount, school, resisted, blocked, absorbed, critical, glancing, crushing
		local unitid, heal
		if eventtype == "SWING_DAMAGE" then
			amount, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
			heal = false
		elseif eventtype == "RANGE_DAMAGE" or eventtype == "SPELL_DAMAGE" or eventtype == "SPELL_PERIODIC_DAMAGE" then
			spellId, spellName, spellSchool, amount, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
			heal = false
		elseif eventtype == "SPELL_HEAL" or eventtype == "SPELL_PERIODIC_HEAL" then
			spellId, spellName, spellSchool, amount, critical = ...
			heal = true
		else
			return
		end
		-- Quickly check if this is a vulnerable NPC.  No point continuing if it is.
		if bit_band(dstFlags, COMBATLOG_FILTER_NPC) == COMBATLOG_FILTER_NPC then
			if self:IsIgnoringVulnerable() and self:IsVulnerable(getNPCID(dstGUID)) then
				return
			end
		end
		--
		if UnitExists(dstName) then
			unitid = dstName
		elseif bit_band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET then
			unitid = "target"
		elseif bit_band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS then
			unitid = "focus"
		end
		self:RecordHit(spellName or spelldata["attack"], amount, dstName, unitid, critical, heal, bit_band(dstFlags, COMBATLOG_FILTER_PVP) == COMBATLOG_FILTER_PVP)
	end
end

function TopScoreFu:OnMenuRequest(level, value)
	if level == 1 then
		dewdrop:AddLine(
			'text', L["MENU_SHOW_SPLASH"],
			'arg1', self,
			'func', "ToggleShowingSplash",
			'checked', self.db.profile.splash
		)
		
		dewdrop:AddLine(
			'text', L["MENU_PLAY_NOISE"],
			'arg1', self,
			'func', "TogglePlayingNoise",
			'checked', self.db.profile.noise
		)
		
		dewdrop:AddLine(
			'text', L["MENU_TAKE_SCREENSHOTS"],
			'arg1', self,
			'func', "ToggleTakingScreenshots",
			'checked', self.db.profile.screenshot
		)
		
		dewdrop:AddLine(
			'text', L["MENU_RESET_SCORES"],
			'arg1', self,
			'func', "ResetScores",
			'closeWhenClicked', true
		)
		
		dewdrop:AddLine()
		
		dewdrop:AddLine(
			'text', L["MENU_FILTER"],
			'hasArrow', true,
			'value', "filter"
		)
		
		dewdrop:AddLine(
			'text', L["MENU_PURGE"],
			'hasArrow', next(self.db.char.hits),
			'value', "purge"
		)
	elseif level == 2 then
		if value == "filter" then
			dewdrop:AddLine(
				'text', L["MENU_VS_MONSTERS"],
				'arg1', self,
				'func', "ToggleOnlyPvP",
				'checked', not self.db.profile.onlyPvP
			)
			
			dewdrop:AddLine(
				'text', L["MENU_INCLUDE_HEALING"],
				'arg1', self,
				'func', "ToggleIncludingHeals",
				'checked', self.db.profile.includeHeals
			)
			
			dewdrop:AddLine(
				'text', L["MENU_INCLUDE_DAMAGE"],
				'arg1', self,
				'func', "ToggleIncludingDamage",
				'checked', self.db.profile.includeDamage
			)
			
			dewdrop:AddLine(
				'text', L["MENU_SHOW_TRIVIAL"],
				'arg1', self,
				'func', "ToggleShowingTrivial",
				'checked', self.db.profile.showTrivial
			)

			dewdrop:AddLine(
				'text', L["MENU_IGNORE_VULNERABLE"],
				'arg1', self,
				'func', "ToggleIgnoreVulnerable",
				'checked', self.db.profile.ignoreVulnerable
			)
			
			dewdrop:AddLine()
			
			for spell, t in pairs(self.db.char.hits) do
				dewdrop:AddLine(
					'text', spell,
					'arg1', self,
					'func', "ToggleFiltering",
					'arg2', spell,
					'checked', not self:IsFiltering(spell)
				)
			end
		elseif value == "purge" then
			for spell, t in pairs(self.db.char.hits) do
				dewdrop:AddLine(
					'text', spell,
					'arg1', self,
					'func', "Purge",
					'arg2', spell
				)
			end
		end
	end
end

local _,class = UnitClass("player")
function TopScoreFu:DoesSpellExist(spell)
	if spell == spelldata["attack"] then
		return true
	end

	for k,v in ipairs(spelldata.substitutions) do
		--ChatFrame3:AddMessage(k.." -> "..v)
		if spell == k then 
			spell = v 
			return true
		end
	end

	local spellexists = spellcache[spell]
	if spellexists == true then
		return true
	else
		return false
	end
	--[[local i = 1
	while true do
		local name = GetSpellName(i, "spell")
		if name == nil or name == "" then
			return false
		elseif name == spell then
			return true
		end
		i = i + 1
	end]]
end

function TopScoreFu:CreateSpellCache()
	-- Blankout/create the table
	spellcache = {}
	local i = 1
	while true do
		local spellname = GetSpellName(i, "spell")
		if spellname == nil or spellname == "" then
			return false
		else
			if not spellcache[spellname] == true then
				spellcache[spellname] = true
			end
		end
		i = i + 1
	end
end
function TopScoreFu:PrintSpellCache()
	for spellname, exists in pairs(spellcache) do
		self:Print("Found: "..spellname)
	end
end

local function copyTable(dest,source)
	for k,v in pairs(source) do
		dest[k] = v
	end
	setmetatable(dest, getmetatable(source))
end

function TopScoreFu:FixIt()
	local ctypes = {
		["normal"] = "table",
		["normalPvP"] = "table",
		["crit"] = "table",
		["critPvP"] = "table",
		["isHeal"] = "boolean",
	}
	-- Go over every recorded spell
	for spell, content in pairs(self.db.char.hits) do
		self:Print(string_format("Processing %s", spell))
		-- Now check that each of the variable types exists for the spell
		for varname, vartype in pairs(ctypes) do
			-- Skip boolean spells
			if type(self.db.char.hits[spell]) ~= "boolean" then
				self:Print(string_format("Checking that %s has a %s entry for vartype %s", spell, varname, vartype))
				--if not type(self.db.char.hits[spell][varname]) == vartype then
				if self.db.char.hits[spell][varname] == nil then
					self:Print(string_format("%s did not have a proper %s for %s, fixing.", spell, vartype, varname))
					-- If we were looking for a table, make a new table
					if vartype == "table" then
						self:Print(string_format("Gave %s a valid %s for %s", spell, vartype, varname))
						self.db.char.hits[spell][varname] = {}
					end
					-- If we were looking for a boolean, set it to false.  Might cause issues, hopefully not.
					if vartype == "boolean" then
						self:Print(string_format("Gave %s a valid %s for %s", spell, vartype, varname))
						self.db.char.hits[spell][varname] = false
					end
				end
			end
		end
	end
end

function TopScoreFu:RecordHit(spell, amount, target, targetid, isCritical, isHeal, pvp)
	if UnitIsCharmed("player") then
		return
	end
	if self.db.char.hits[spell] == false then
		return
	elseif self.db.char.hits[spell] == nil then
		if not self:DoesSpellExist(spell) then
			self.db.char.hits[spell] = false
			return
		end
		self.db.char.hits[spell] = {
			crit = {},
			critPvP = {},
			normal = {},
			normalPvP = {},
			isHeal = isHeal,
		}
	end
	local t
	local tPvP
	if isCritical then
		t = self.db.char.hits[spell].crit
		tPvP = self.db.char.hits[spell].critPvP
	else
		t = self.db.char.hits[spell].normal
		tPvP = self.db.char.hits[spell].normalPvP
	end
	if t.amount == nil or t.amount < amount or (pvp and (tPvP.amount == nil or tPvP.amount < amount)) then
		if t.amount and t.amount > amount then
			-- this is a pvp record but not a pve record; don't overwrite the savedvars for pve
			-- hacky solution that creates an extra table :(
			t = {}
		end
		local level
		local class
		if not targetid then
			if UnitName("target") == target then
				targetid = "target"
			else
				return
			end
		end
		if targetid ~= nil then
			level = UnitLevel(targetid)
			_,class = UnitClass(targetid)
			if not self.db.profile.showTrivial and UnitIsTrivial(targetid) then
				return
			end
			-- This check is now done MUCH earlier.
			--if self:IsIgnoringVulnerable() and self:IsVulnerable(target) then
			--	return
			--end
			t.amount = amount
			t.target = target
			t.level = level
			t.class = class
			if pvp and UnitPlayerControlled(targetid) then
				copyTable(tPvP, t)
			end
			if not self:IsFiltering(spell) and (not isHeal or self.db.profile.includeHeals) and (isHeal or self.db.profile.includeDamage) and (pvp or not self.db.profile.onlyPvP) then
				if isCritical then
					self:Pour(string_format(L["PATTERN_NEW_CRITICAL_RECORD"], spell, amount), 1, 1, 0)
				else
					self:Pour(string_format(L["PATTERN_NEW_NORMAL_RECORD"], spell, amount), 1, 1, 0)
				end
				if self.db.profile.splash then
					if isCritical then
						TopScoreFuSplash:AddMessage(string_format(L["PATTERN_NEW_CRITICAL_RECORD"], spell, amount), 1, 1, 0, 1, 3)
					else
						TopScoreFuSplash:AddMessage(string_format(L["PATTERN_NEW_NORMAL_RECORD"], spell, amount), 1, 1, 0, 1, 3)
					end
				end
				if self.db.profile.noise then
					PlaySound("LEVELUP")
				end
				if self.db.profile.screenshot then
					TakeScreenshot()
				end
				self:Update()
			end
		end
	end
end

function TopScoreFu:UpdateData()
	self.highCrit = 0
	self.highNormal = 0
	for spell, t in pairs(self.db.char.hits) do
		if t and not self:IsFiltering(spell) and (not t.isHeal or self.db.profile.includeHeals) and (t.isHeal or self.db.profile.includeDamage) then
			local crit = t.crit
			local normal = t.normal
			if self.db.profile.onlyPvP then
				crit = t.critPvP
				normal = t.normalPvP
			end
			if crit ~= nil and crit.amount ~= nil and self.highCrit < crit.amount then
				self.highCrit = crit.amount
			end
			if normal ~= nil and normal.amount ~= nil and self.highNormal < normal.amount then
				self.highNormal = normal.amount
			end
		end
	end
end

function TopScoreFu:UpdateText()
	self:SetText(string_format("|cffffffff%d|r/|cffffffff%d|r", self.highCrit, self.highNormal))
end

function TopScoreFu:OnTooltipUpdate()
	local addedHint = false
	for spell, t in pairs(self.db.char.hits) do
		if t and not self:IsFiltering(spell) and (not t.isHeal or self.db.profile.includeHeals) and (t.isHeal or self.db.profile.includeDamage) then
			local crit = t.crit
			local normal = t.normal
			if self.db.profile.onlyPvP then
				crit = t.critPvP
				normal = t.normalPvP
			end
			if (crit and crit.amount) or (normal and normal.amount) then
				local cat = tablet:AddCategory(
					'text', spell,
					'columns', 2,
					'child_textR', 1,
					'child_textG', 1,
					'child_textB', 0,
					'func', "SpamSingleScore",
					'arg1', self,
					'arg2', spell,
					'arg3', normal and normal.amount or 0,
					'arg4', crit and crit.amount or 0
				)
				
				if crit ~= nil and crit.amount ~= nil then
					local color = "ffffff"
					if crit.amount == self.highCrit then
						color = "00ff00"
					end
					local target = crit.target
					if target == "player" then
						target = UnitName("player")
					end
					local level = crit.level
					if level == -1 then
						level = "?"
					end
					cat:AddLine(
						'text', L["TEXT_CRITICAL"] .. " [|cff" .. color .. crit.amount .. "|r]",
						'text2', target .. " [|cffffffff" .. level .. "|r]",
						'text2R', RAID_CLASS_COLORS[crit.class]["r"],
						'text2G', RAID_CLASS_COLORS[crit.class]["g"],
						'text2B', RAID_CLASS_COLORS[crit.class]["b"]
					)
				end
				if normal ~= nil and normal.amount ~= nil then
					local color = "ffffff"
					if normal.amount == self.highNormal then
						color = "00ff00"
					end
					local target = normal.target
					if target == "player" then
						target = UnitName("player")
					end
					local level = normal.level
					if level == -1 then
						level = "?"
					end
					cat:AddLine(
						'text', L["TEXT_NORMAL"] .. " [|cff" .. color .. normal.amount .. "|r]",
						'text2', target .. " [|cffffffff" .. level .. "|r]",
						'text2R', RAID_CLASS_COLORS[normal.class]["r"],
						'text2G', RAID_CLASS_COLORS[normal.class]["g"],
						'text2B', RAID_CLASS_COLORS[normal.class]["b"]
					)
				end
			end
			if not addedHint then
				addedHint = true
				tablet:SetHint(L["HINT"])
			end
		end
	end
end
	
function TopScoreFu:OnClick()
	if IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
		local critSpell, critAmount, normalSpell, normalAmount
		self:UpdateData()
		for spell, t in pairs(self.db.char.hits) do
			if t and not self:IsFiltering(t) and (not t.isHeal or self.db.profile.includeHeals) and (t.isHeal or self.db.profile.includeDamage) then
				local crit = t.crit
				local normal = t.normal
				if self.db.profile.onlyPvP then
					crit = t.critPvP
					normal = t.normalPvP
				end
				if crit.amount == self.highCrit then
					critSpell = spell
					critAmount = crit.amount
				end
				if normal.amount == self.highNormal then
					normalSpell = spell
					normalAmount = normal.amount
				end
			end
		end
		local s = ""
		if normalSpell ~= nil then
			s = s .. string_format(L["PATTERN_NORMAL_SPELL"], normalSpell) .. ": " .. normalAmount
		end
		if critSpell ~= nil then
			if s ~= "" then
				s = s .. " || "
			end
			s = s .. string_format(L["PATTERN_CRITICAL_SPELL"], critSpell) .. ": " .. critAmount
		end
		ChatFrameEditBox:Insert(s)
	end
end

-- Credit to Aileen for this.
function TopScoreFu:IsVulnerable(target)
	-- Check for Heroic mobs first.
	if IsInInstance() and (GetInstanceDifficulty() == 2) then
		if heroicVulnerableMobs[target] then
			return true
		end
		--for _, heroicBoss in ipairs(heroicVulnerableBosses) do
		--	if target == heroicBoss then
		--		return true
		--	end
		--end
	end
	-- Regular mobs check.
	if vulnerableMobs[target] then
		return true
	end
	--for _, boss in ipairs(vulnerableBosses) do
	--	if target == boss then
	--		return true
	--	end
	--end
	--for _, mob in ipairs(L["VULNERABLE_MOBS"]) do
	--	if target == mob then
	--		return true
	--	end
	--end
	-- Fall back to returning false if nothing matched.
	return false
end

-- Credit to TheKarn for this function.
function TopScoreFu:SpamSingleScore(spell, normal, crit)
	if spell and (normal or crit) then
		local str = spell
		if normal ~= 0 then str = str.." - "..L["TEXT_NORMAL"]..": "..normal end
		if crit ~= 0 then str = str.." - "..L["TEXT_CRITICAL"]..": "..crit end
		if not ChatFrameEditBox:IsVisible() then ChatFrameEditBox:Show() end
		ChatFrameEditBox:Insert(str.." ")
	end
end
